#pragma once
#include <string>
#include <vector>
#include <map>
#include "cpputil/cpputil/cpputil.h"

enum SectionType {
	SECTION_TEXT,
	SECTION_DATA,
	SECTION_RODATA,
	SECTION_BSS
};

enum SizeType {
	SIZE_16,
	SIZE_32,
	SIZE_64
};

class Section {
private:
	std::string _name;
	uint64_t _addr;
	SectionType _sectionType;
	SizeType _sizeType;
	BinBuffer _data;
public:
	Section();
	Section(std::string name, uint64_t addr, SectionType sectionType, SizeType sizeType, BinBuffer data);
	std::string name();
	uint64_t& addr();
	SectionType sectionType();
	SizeType sizeType();
	BinBuffer& data();
};

class Symbol {
private:
	uint64_t _fileId;
	std::string _name;
	std::string _sectionName;
	uint64_t _sectionOffset;
public:
	Symbol();
	Symbol(uint64_t fileId, std::string name, std::string sectionName, uint64_t sectionOffset);
	uint64_t fileId();
	std::string name();
	std::string sectionName();
	uint64_t sectionOffset();
};

class Relocation {
private:
	std::string _symbolName;
	std::string _dstSectionName;
	uint64_t _dstSectionOffset;
	bool _isRelative;
public:
	Relocation(std::string symbolName, std::string dstSectionName, uint64_t dstSectionOffset, bool isRelative);
	std::string symbolName();
	std::string dstSectionName();
	uint64_t dstSectionOffset();
	bool isRelative();
};

template<typename T>
class ArrayMap {
public:
	std::map<uint64_t, std::vector<T>> data;
};

class Linker {
private:
	uint64_t fileId;
	ArrayMap<Section> sections;
	ArrayMap<Symbol> in_symbols;
	ArrayMap<Symbol> out_symbols;
	ArrayMap<Relocation> relocations;
	bool findSymbol(ArrayMap<Symbol> symbols, std::string name, Symbol& res);
	bool findSymbol(ArrayMap<Symbol> symbols, uint64_t fileId, std::string name, Symbol& res);
	void parseCmnFile(File& file);
	void parseElf32File(File& file, bool is16mode);
	uint64_t _startAddr;
public:
	Linker();
	Linker& parse(std::string filename, bool is16mode = false);
	Linker& addSection(uint64_t fileId, Section section);
	Linker& addInSymbol(uint64_t fileId, Symbol symbol);
	Linker& addOutSymbol(uint64_t fileId, Symbol symbol);
	Linker& addRelocation(uint64_t fileId, Relocation relocation);
	Linker& link(uint64_t baseAddr, std::string start_symbol = "");
	Linker& makeBin(std::string filename);
	Linker& makeFbin(std::string filename);
};